/*
 * =====================================================================================
 *
 *       Filename:  getProcInfo.c
 *
 *    Description:  Modul poskytuje uzivatelske aplikaci informace o procesu.
 *                  Jako komunikacni prostredek slouzi soubor v /proc.
 *                  Do nej se zapise pid pozadovaneho procesu a pote prectou informace.
 *
 *        Created:  19.4.2013 10:27:00
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Radek Necas, xnecas18@stud.fit.vutbr.cz
 *   Organization:  FIT, VUT Brno
 *
 * =====================================================================================
 */

#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/sched.h>

#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>

#include "../../include/const.h"

const struct task_struct *savedTask;
char *comm;
int commLen;
char wantedPid[MAX_PID_LEN];
char wasWrite;

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Radek Necas");
MODULE_DESCRIPTION("Get info about process by pid");

/*
 * Funkce ziska task_struct dle zadaneho pid
 */
struct task_struct *findByPid(int wantedPid)
{
/*    
    struct task_struct *task;
  
    for_each_process(task) {
        if(task->pid == wantedPid) {
            return task;
        } 
    }
*/

    return pid_task(find_vpid(wantedPid), PIDTYPE_PID);
    //return NULL;
}

/*
 * Funkce ulozi informace do globalni promenne comm.
 * @return OK TRUE, jinak FALSE
 */
int storeInfo(const struct task_struct *task)
{
    int len, i; 
    commLen = strlen(task->comm)+1;
    if(comm != NULL) {
        kfree(comm);
    }
    comm = (char *)kmalloc(commLen*sizeof(char), GFP_KERNEL);
    if(comm == NULL) {
        printk(KERN_INFO "getProcInfo: Chyba pri alokaci pameti");
        return FALSE;
    }

    snprintf(comm, commLen, "%s", task->comm);
    return TRUE;
}

/*
 * Funkce, ktera bude volana vzdy pri cteni souboru.
 * Soubor je umisten v /proc a slouzi jako komunikacni prostredek
 * mezi modulem a uzivatelskou aplikaci
 */
static int procInfoRead(char *page, char **start, off_t off, int len,
        int *eof, void *data)
{
    int endIndex;
    int writeLen;
    int ret;


    // Pokud nebylo nikdy zapsano, neni znam proces o kterem hledame info
    if(!wasWrite) {
            printk(KERN_INFO "getProcInfo: Zadost o cteni pred zapisem");
            printk(KERN_INFO "");
        return -1;
    }
    
    // Jeste jsme nenacetly vsechny informace
    if(off <= commLen) {
        endIndex = off + len;
        writeLen = len;
        if(endIndex >= commLen) {   // Dostali bysme se za hranice textu
            writeLen = commLen - off;
            *eof = 1;
            ret = snprintf(page, writeLen, "%s", &savedTask->comm[off]); 

        }
        else {
            ret = snprintf(page, writeLen, "%s", &savedTask->comm[off]); 

            printk(KERN_INFO "");
        }
    }
    else {
        printk(KERN_INFO "procsInfo: procInfoRead zadost o cteni mimo meze");
        printk(KERN_INFO "");
        *eof = 1;
        return 0;
    }
    
    return ret;
}

/* Funkce, ktera bude volana pri zapisovani do souboru.
 * Soubor bude umisten v /proc a slouzi jako komunikacni prostredek
 * modulu s uzivatelskou aplikaci
 */
static int procInfoWrite(struct file *file, const char *buff,
        unsigned long len, void *data)
{
    long pid;

    if(len > MAX_PID_LEN) {
        printk(KERN_INFO "procsInfo: procInfoWrite zadost o zapisu prilis velkych dat");
        printk(KERN_INFO "");
        return -1;
    }

    // Ziskani pid textove
    if(copy_from_user(wantedPid, buff, len)) {
        return -2;
    }

    // Prevedeni pid + testy
    if(strict_strtol(wantedPid, 10, &pid) < 0) {
        printk(KERN_INFO "procsInfo: procInfoWrite chybne pid");
        printk(KERN_INFO "");
        return -3;
    }

    if(pid <= 0 || pid > MAX_PID) {
        printk(KERN_INFO "procsInfo: procInfoWrite pid mimo meze");
        printk(KERN_INFO "");
        return -3;
    }

    // Nalezeni prislusne task_struct
    if((savedTask = findByPid(pid)) == NULL) {
        printk(KERN_INFO "procsInfo: nepodarilo se najit task_struct s pid %d", pid);
        printk(KERN_INFO "");
        return -3;
    }


    printk(KERN_INFO "state: %d, exit_state: %d", savedTask->state, savedTask->exit_state);
    printk(KERN_INFO "");


    if(storeInfo(savedTask) <= 0) {
        return -3;
    }
    
    wasWrite = 1;
    return len;
}


/* 
 * Inicializacni funkce pro modul.
 */
int init_module(void)
{
    struct proc_dir_entry *procInfoFile;

    procInfoFile = create_proc_entry(PROCS_INFO_FILENAME, 0666, NULL);
    if(procInfoFile == NULL) {
        printk(KERN_INFO "getProcInfo: Nepodarilo se vytvorit soubor v proc");
        printk(KERN_INFO "");
        return -1;
    }

    procInfoFile->read_proc = procInfoRead;
    procInfoFile->write_proc = procInfoWrite;
    wasWrite = 0;
    comm = NULL;

    printk(KERN_INFO "getProcInfo: Modul zaveden");
    printk(KERN_INFO "");
    return 0;
}

/* Funkce pro uklid modulu
 */
void cleanup_module(void)
{
    remove_proc_entry(PROCS_INFO_FILENAME, NULL);
    if(comm != NULL) {
        kfree(comm);
    }

    printk(KERN_INFO "getProcInfo: Modul odebran");
    printk(KERN_INFO "");
}
